home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 October / Macworld (1998-10).dmg / Shareware World / Info / For Developers / MacZoop 1.8.4 / More Classes / File Classes / ZFolderScanner.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-02  |  13.0 KB  |  509 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZFolderScanner.cpp    -- a generic object for recursively searching folders
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            Ā© 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "ZFolderScanner.h"
  23. #include    "ZProgress.h"
  24. #include    "MacZoop.h"
  25. #include    "FileMgrUtils.h"
  26.  
  27. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  28.  
  29. ZFolderScanner::ZFolderScanner( const FSSpec& rootFolder )
  30.     : ZFile( rootFolder )
  31. {
  32.     Boolean        isDirectory;
  33.     
  34.     classID = CLASS_ZFolderScanner;
  35.     
  36.     // check that the spec is a folder- throws exception if not.
  37.     
  38.     FailOSErr( FSpGetDirectoryID ( &itsSpec, &topDirID, &isDirectory ));
  39.     FailOSErr( (isDirectory)? noErr : paramErr );
  40.     
  41.     curDepth = 0;
  42.     searchDepth = kDefaultSearchDepth;    
  43.     useProgressDialog = TRUE;
  44. }
  45.  
  46. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  47.  
  48.  
  49. ZFolderScanner::ZFolderScanner()
  50.     : ZFile( "\p" )
  51. {
  52.     // assume the filespec for the start-up hard disk.
  53.     
  54.     classID = CLASS_ZFolderScanner;
  55.  
  56.     HParamBlockRec    hb;
  57.     
  58.     hb.volumeParam.ioCompletion = NULL;
  59.     hb.volumeParam.ioVolIndex = 1;
  60.     hb.volumeParam.ioNamePtr = fName;
  61.     fName[0] = 0;
  62.     
  63.     FailOSErr( PBHGetVInfoSync( &hb ));
  64.     
  65.     // make a filespec. This is a volume, so to make sure all goes
  66.     // OK, we actually force the volume name to have an appended colon
  67.     // and set the parent ID to -2.
  68.     
  69.     itsSpec.vRefNum = hb.volumeParam.ioVRefNum;
  70.     itsSpec.parID = -2;
  71.     BlockMoveData(fName, itsSpec.name, fName[0] + 1);
  72.     itsSpec.name[fName[0] + 1] = ':';
  73.     itsSpec.name[0] += 1;
  74.  
  75.     curDepth = 0;
  76.     searchDepth = kDefaultSearchDepth;    
  77.     useProgressDialog = TRUE;
  78. }
  79.  
  80.  
  81. /*---------------------------------***  DESTRUCTOR  ***---------------------------------*/
  82.  
  83. ZFolderScanner::~ZFolderScanner()
  84. {
  85. }
  86.  
  87.  
  88. /*------------------------------***  SETSEARCHDEPTH  ***-------------------------------*/
  89. /*
  90. set how many levels you want to recurse to when scanning. The default is 0, meaning only
  91. scan the folder and no folders within it. Passing -1 means search every folder no matter
  92. how deep the search goes. Other positive values set the scanning depth.
  93. ---------------------------------------------------------------------------------------*/
  94.  
  95. void    ZFolderScanner::SetSearchDepth( const short aSearchDepth )
  96. {
  97.     searchDepth = aSearchDepth;
  98. }
  99.  
  100. /*--------------------------------***  PICKFOLDER  ***---------------------------------*/
  101. /*
  102. presents an interface for selecting a folder to scan
  103. ---------------------------------------------------------------------------------------*/
  104.  
  105. Boolean    ZFolderScanner::PickFolder()
  106. {
  107.     FSSpec    aSpec;
  108.     Boolean    result = FALSE;
  109.     
  110.     if( ChooseFolder( &aSpec ))
  111.     {
  112.         itsSpec = aSpec;
  113.         result = TRUE;
  114.     }
  115.     
  116.     return result;
  117. }
  118.  
  119.  
  120. /*--------------------------------***  SCANFOLDER  ***---------------------------------*/
  121. /*
  122. public call to kick off the folder scan after setting it up.
  123. ---------------------------------------------------------------------------------------*/
  124.  
  125. void    ZFolderScanner::ScanFolder()
  126. {
  127.     Boolean    isDirectory;
  128.     
  129.     // set up the <pb> data member for scanning
  130.  
  131.     pb.hFileInfo.ioNamePtr = fName;
  132.     pb.hFileInfo.ioVRefNum = itsSpec.vRefNum;
  133.     fName[0] = 0;
  134.     curDepth = 0;
  135.     
  136.     FailOSErr( FSpGetDirectoryID( &itsSpec, &topDirID, &isDirectory ));
  137.     FailOSErr( (isDirectory)? noErr : paramErr );
  138.     
  139.     // set up progress dialog, if wanted
  140.     
  141.     if( useProgressDialog )
  142.     {
  143.         FailNIL( itsPD = new ZProgress( gApplication, kStdProgressResID, _STRIPED_BAR, kIndeterminateProgress, kStopType ));
  144.         itsPD->SetTitle( itsSpec.name );
  145.     }
  146.     
  147.     // call Scan1Folder, which recurses to the set depth if need be.
  148.     
  149.     try
  150.     {
  151.         Scan1Folder( topDirID );
  152.     }
  153.     catch( OSErr err )
  154.     {
  155.         // an error occurred, or the user hit "Stop"
  156.         
  157.         if( useProgressDialog )
  158.             ForgetObject( itsPD );
  159.         
  160.         // main exception handler doesn't display alert for cancel message,
  161.         // so it is quite safe to throw it in any case
  162.         
  163.         throw err;
  164.     }
  165.     
  166.     if( useProgressDialog )
  167.         ForgetObject( itsPD );
  168. }
  169.  
  170.  
  171. /*-------------------------------***  SCAN1FOLDER  ***---------------------------------*/
  172. /*
  173. internal method that calls itself recursively to implement a scan of folders more than
  174. 1 level deep. THe recursion depth can be controlled by the searchDepth data member.
  175. ---------------------------------------------------------------------------------------*/
  176.  
  177. void    ZFolderScanner::Scan1Folder( const long dirID )
  178. {
  179.     short    index = 1;
  180.     OSErr    theErr;
  181.     
  182.     curDepth++;    // gone down a level.
  183.     
  184.     // for each item in the folder passed, either pass it to Process1File, or call
  185.     // this method again recursively.
  186.     
  187.     do
  188.     {
  189.         pb.dirInfo.ioFDirIndex = index++;    // iterate through this folder
  190.         pb.dirInfo.ioDrDirID = dirID;        // this is the folder to look in
  191.         
  192.         theErr = PBGetCatInfoSync( &pb );    
  193.         
  194.         // if the err is 'fnfErr', then this only means we tried to get more files than
  195.         // there were in the folder. Thus this is not something we should throw.
  196.         
  197.         if (( theErr != noErr ) && ( theErr != fnfErr ))
  198.             FailOSErr( theErr );
  199.             
  200.         // what have we got there?
  201.         
  202.         if( theErr == noErr )
  203.         {
  204.             FSSpec    aSpec;
  205.             
  206.             if (( pb.hFileInfo.ioFlAttrib & ioDirMask ) != 0 )
  207.             {
  208.                 // another folder within this one- recurse if we have depth in hand. If
  209.                 // search depth is -1, we search as deep as necessary.
  210.                 
  211.                 if (( curDepth < searchDepth ) ||
  212.                     ( searchDepth == kScanEveryFolderInHierarchy ))
  213.                 {
  214.                     FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
  215.                     
  216.                     Process1Folder( aSpec );
  217.                     Scan1Folder( pb.dirInfo.ioDrDirID );
  218.                 }
  219.             }
  220.             else
  221.             {
  222.                 // a file, so call the method to handle the file. First we make a nice FSSpec
  223.                 // for the file in case we want to make an ZFile object, etc.
  224.                 
  225.                 FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
  226.                 Process1File( aSpec, pb.hFileInfo.ioFlFndrInfo.fdType );
  227.             }
  228.         }
  229.     }
  230.     while( theErr == noErr );
  231.     
  232.     // leaving, so decrement the level counter
  233.     
  234.     --curDepth;
  235. }
  236.  
  237. /*------------------------------***  PROCESS1FOLDER  ***-------------------------------*/
  238. /*
  239. called for every new folder before it is scanned. The default method does nothing, but
  240. you may want to override it for special uses.
  241. ---------------------------------------------------------------------------------------*/
  242.  
  243.  
  244. void    ZFolderScanner::Process1Folder( const FSSpec& aSpec )
  245. {
  246.     SendMessage( msgFolderscanProcess1Folder, (void*) &aSpec );
  247. }
  248.  
  249.  
  250. /*-------------------------------***  PROCESS1FILE  ***--------------------------------*/
  251. /*
  252. called for every file in the scanned folders. Override to do something useful with the
  253. file.
  254. ---------------------------------------------------------------------------------------*/
  255.  
  256. void    ZFolderScanner::Process1File( const FSSpec& aSpec, const OSType fType )
  257. {
  258.     // this method is called for every file found in the folder search. The default method
  259.     // doesn't do anything, but you will override this to process those files of interest.
  260.     // Note that folders are not passed to this function, only files. A common action at
  261.     // this point is to make a ZFile object with the FSSpec, then read the file, etc.
  262.     // Naturally your overridden method can really do what it likes. If you are only interested
  263.     // in particular file types, for example, you can simply ignore those that you don't want.
  264.     // if you want to use the progress dialog, you can call the inherited method to maintain
  265.     // it. By default, this passes each filename as a message, and uses the "barber-pole" style.
  266.     
  267.     if( useProgressDialog )
  268.     {
  269.         itsPD->SetMessage( aSpec.name );
  270.         FailOSErr( itsPD->InformProgress( 0 )? noErr : userCanceledErr );
  271.     }
  272.     
  273.     SendMessage( msgFolderscanProcess1File, (void*) &aSpec );
  274. }
  275.  
  276.  
  277.  
  278. #pragma mark -
  279.  
  280.  
  281.  
  282. Boolean        ChooseFolder( FSSpec* folderSpec )
  283. {
  284.     Boolean        result = FALSE;
  285.     OSErr        err;
  286.     
  287.     #if _USE_NAVIGATION_SERVICES
  288.     
  289.     if ( gMacInfo.hasNavigationServices )
  290.     {
  291.         // use groovy new folder selector dialog...
  292.         
  293.         NavDialogOptions    navOptions;
  294.         NavReplyRecord        navReply;
  295.         
  296.         FailOSErr( NavGetDefaultDialogOptions( &navOptions ));
  297.         gApplication->GetName( navOptions.clientName );
  298.         navOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
  299.         gWindowManager->Deactivate();
  300.         
  301.         err = NavChooseFolder(    NULL,
  302.                                 &navReply,
  303.                                 &navOptions,
  304.                                 gNavEventHandler,
  305.                                 NULL,
  306.                                 0L );
  307.                                 
  308.         gWindowManager->Activate();
  309.         
  310.         if (( err == noErr ) && navReply.validRecord )
  311.         {
  312.             // extract the FSSpec:
  313.             
  314.             AEDesc        specDesc;
  315.             FSSpec        dirSpec;
  316.             CInfoPBRec     pb;
  317.     
  318.             FailOSErr( AEGetNthDesc( &navReply.selection, 1, typeFSS, NULL, &specDesc ));
  319.             BlockMoveData( *specDesc.dataHandle, &dirSpec, sizeof( FSSpec ));
  320.             
  321.             // to return the FSSpec in a compatible way, we need to do more than simply
  322.             // return this spec. The <parID> field of <dirSpec> is the directory ID of the
  323.             // chosen folder- what this function is supposed to return is the full FSSpec:
  324.             
  325.             pb.dirInfo.ioNamePtr = folderSpec->name;
  326.             pb.dirInfo.ioVRefNum = dirSpec.vRefNum;
  327.             pb.dirInfo.ioCompletion = NULL;
  328.             pb.dirInfo.ioFDirIndex = -1;
  329.             pb.dirInfo.ioDrDirID = dirSpec.parID;
  330.             
  331.             err = PBGetCatInfoSync( &pb );
  332.             
  333.             folderSpec->parID = pb.dirInfo.ioDrParID;
  334.             folderSpec->vRefNum = pb.dirInfo.ioVRefNum;
  335.             
  336.             result = TRUE;
  337.         }
  338.         
  339.         FailOSErr( NavDisposeReply( &navReply ));
  340.     }
  341.     else
  342.     {
  343.     #endif
  344.     
  345.     Point                where = {-1, -1};
  346.     FileFilterYDUPP        ffUPP;
  347.     DlgHookYDUPP        dhUPP;
  348.     tFolderInfo            folderInfo;
  349.     short                dummyActiveList = 0;
  350.     
  351.     gWindowManager->DeactivateForDialog( kPickFolderDialogID );
  352.     
  353.     ffUPP = NewFileFilterYDProc((ProcPtr) GetDirFileFilter);
  354.     dhUPP = NewDlgHookYDProc((ProcPtr) GetDirDlgHook);
  355.     
  356.     folderInfo.selectHit = FALSE;
  357.     folderInfo.dirFlag = FALSE;
  358.  
  359.     CustomPutFile("\p","\p",
  360.                     &folderInfo.aReply,
  361.                     kPickFolderDialogID,
  362.                     where,
  363.                     dhUPP,
  364.                     NULL,
  365.                     &dummyActiveList,
  366.                     NULL,
  367.                     &folderInfo);
  368.                     
  369.     DisposeRoutineDescriptor(ffUPP);
  370.     DisposeRoutineDescriptor(dhUPP);
  371.     
  372.     if (folderInfo.selectHit)
  373.     {
  374.         if ( !folderInfo.aReply.sfIsFolder && !folderInfo.aReply.sfIsVolume )
  375.         {
  376.             FSSpec        tempSpec;
  377.             CInfoPBRec    infoPB;
  378.             Boolean        isDirectory;
  379.     
  380.             tempSpec = folderInfo.aReply.sfFile;
  381.             
  382.             if (tempSpec.name[0] != '\0')
  383.                 return FALSE; //err
  384.     
  385.             infoPB.dirInfo.ioNamePtr = tempSpec.name;
  386.             infoPB.dirInfo.ioVRefNum = tempSpec.vRefNum;
  387.             infoPB.dirInfo.ioDrDirID = tempSpec.parID;
  388.             infoPB.dirInfo.ioFDirIndex = -1;
  389.             
  390.             err = PBGetCatInfoSync( &infoPB );
  391.             if (err)
  392.                 return FALSE;
  393.             
  394.             tempSpec.parID = infoPB.dirInfo.ioDrParID ;
  395.             
  396.             // make sure that it's a directory
  397.             
  398.             isDirectory = (infoPB.dirInfo.ioFlAttrib & 0x10) ;
  399.             if ( !isDirectory )
  400.                 return FALSE; //err
  401.             
  402.             folderInfo.aReply.sfFile = tempSpec ;
  403.             folderInfo.aReply.sfScript = infoPB.dirInfo.ioDrFndrInfo.frScript ;
  404.             folderInfo.aReply.sfFlags = infoPB.dirInfo.ioDrUsrWds.frFlags ;
  405.             folderInfo.aReply.sfIsFolder = (tempSpec.parID == 1) ? (0x00) : (0xFF) ;
  406.             folderInfo.aReply.sfIsVolume = (tempSpec.parID == 1) ? (0xFF) : (0x00) ; ;
  407.         }
  408.  
  409.         *folderSpec = folderInfo.aReply.sfFile;
  410.         
  411.         gWindowManager->Activate();
  412.         result = TRUE;
  413.     }    
  414.     
  415.     #if _USE_NAVIGATION_SERVICES
  416.     }
  417.     #endif
  418.     
  419.     return result;
  420. }
  421.  
  422. /*****************************************************************************/
  423.  
  424.  
  425. static pascal Boolean    GetDirFileFilter(ParmBlkPtr pb,tFolderInfo* fInfo)
  426. {
  427.     return ((pb->fileParam.ioFlAttrib & ioDirMask) == 0);
  428. }
  429.  
  430. /*****************************************************************************/
  431.  
  432.  
  433. static pascal short        GetDirDlgHook(short item, DialogPtr theDialog, tFolderInfo* fInfo)
  434. {
  435.     short            itemType;
  436.     Handle            itemHand;
  437.     Rect            itemBox;
  438.     FSSpec            fRef;
  439.     static FSSpec    lastFile;
  440.     
  441.     if (GetWRefCon(theDialog) == (long) sfMainDialogRefCon)
  442.     {
  443.         fRef = fInfo->aReply.sfFile;
  444.         
  445.         switch (item)
  446.         {
  447.             case sfHookFirstCall:
  448.                 lastFile.vRefNum = -9999;
  449.                 break;
  450.             case sfHookLastCall:
  451.                 break;
  452.             default:
  453.                 break;
  454.             case sfHookNullEvent:
  455.                 if (! SameFile(&fRef,&lastFile))
  456.                 {
  457.                     lastFile = fRef;
  458.                     
  459.                     MakeCanonFSSpec(&fRef);    
  460.                     GetDialogItem(theDialog, kPickFolderButton, &itemType, &itemHand, &itemBox);
  461.                     SetSFButtonTitle((ControlHandle) itemHand, &fRef, &itemBox);
  462.                 }
  463.                 break;
  464.             case kPickFolderButton:
  465.                 fInfo->selectHit = TRUE;
  466.                 item = sfItemCancelButton;
  467.                 break;
  468.         }
  469.     }
  470.     return item;
  471. }
  472.  
  473. /*****************************************************************************/
  474.  
  475. static void        SetSFButtonTitle(ControlHandle theButton,FSSpec* theFile,Rect* buttonRect)
  476. {
  477.     // sets the button title to <Select ā€œ<name>ā€>, but truncated to fit the rect.
  478.     
  479.     short            width,saveSize,saveFont;
  480.     StringHandle    nStr;
  481.     Str255            bStr;
  482.     
  483.     saveSize = qd.thePort->txSize;
  484.     saveFont = qd.thePort->txFont;
  485.     
  486.     TextSize(12);
  487.     TextFont(0);
  488.     
  489.     nStr = GetString(kStdButtonTextStrID);
  490.     HLock((Handle) nStr);
  491.     CopyPString(*nStr, bStr);
  492.     HUnlock((Handle) nStr);
  493.     ReleaseResource((Handle) nStr);
  494.  
  495.     width = buttonRect->right - buttonRect->left - 32 - StringWidth(bStr);
  496.     TruncString(width, theFile->name, smTruncMiddle);
  497.     
  498.     ConcatPStrings(bStr,theFile->name);
  499.     ConcatPStrings(bStr,"\pā€");
  500.  
  501.     SetControlTitle(theButton,bStr);
  502.     ValidRect(buttonRect);
  503.     
  504.     TextFont(saveFont);
  505.     TextSize(saveSize);
  506. }
  507.  
  508.  
  509.